
package com.zpz.framework.zpzoauth.service.impl;

import com.alibaba.fastjson.JSON;
import com.zpz.framework.zpzoauth.common.redis.ZpzOauthRedisService;
import com.zpz.framework.zpzoauth.common.result.ZpzOauthResultStatus;
import com.zpz.framework.zpzoauth.common.result.ZpzOauthResultMsg;
import com.zpz.framework.zpzoauth.common.utils.*;
import com.zpz.framework.zpzoauth.dao.ZpzOauthFrameAccessMapper;
import com.zpz.framework.zpzoauth.dao.ZpzOauthFrameReleaseAccessMapper;
import com.zpz.framework.zpzoauth.pojo.po.FrameAccess;
import com.zpz.framework.zpzoauth.pojo.po.FrameReleaseAccess;
import com.zpz.framework.zpzoauth.pojo.po.OauthClientDetails;
import com.zpz.framework.zpzoauth.pojo.vo.GetAccessByRoleVo;
import com.zpz.framework.zpzoauth.service.ZpzOauthFrameAccessService;
import com.zpz.framework.zpzoauth.service.ZpzOauthPermissionService;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangpanzhi
 * @time 2019-07-09
 * @description 未经本人允许请勿随便改动，尊重劳动
 * */
@Service("permissionService")
public class ZpzOauthPermissionServiceImpl implements ZpzOauthPermissionService {
    private static final Log log = LogFactory.getLog(ZpzOauthPermissionServiceImpl.class);
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    public static String APPLICATION_NAME;
    @Value("${zpzOauth.redis.pre}")
    public void setApplicationName(String applicationName){
        APPLICATION_NAME=applicationName;
    }

    @Resource
    private ZpzOauthFrameAccessService zpzOauthFrameAccessService;
    @Resource
    private ZpzOauthFrameAccessMapper zpzOauthFrameAccessMapper;
    @Resource
    private ZpzOauthFrameReleaseAccessMapper zpzOauthFrameReleaseAccessMapper;
    @Resource
    private ZpzOauthFilterReleaseAccess zpzOauthFilterReleaseAccess;
    @Resource
    private ZpzOauthRedisService zpzOauthRedisService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private ZpzOauthRequestUtil zpzOauthRequestUtil;
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) throws Exception{
        String lience = request.getHeader(ZpzOauthCommonConstant.REQUEST_HEADER_LIENCE);
        log.info("：：：：：权限认证：：：的请求路径：：：：：："+request.getRequestURI());
        String clientId=null;
        String apicode=null;
        String ucode=null;
        UserAgent userAgent = request !=null ?UserAgent.parseUserAgentString(request.getHeader("User-Agent")):null;
        String ipAddress = ZpzOauthRequestUtil.getIpAddress(request);
        log.info("业务请求["+apicode+"]：::::::::::请求设备信息(IP::"+ipAddress+")："+JSON.toJSONString(userAgent));
        try {
            if (StringUtils.isNotBlank(lience)){
               String _license = new String(Base64.decodeBase64(lience));
                log.info("业务请求["+apicode+"]：:：：：：：权限认证：：：获取的头部license  base64解完码后：：：：：："+ JSON.toJSONString(_license));
               String[] liencese = _license.split(":");
               clientId=liencese[0];
               apicode=liencese[1];
               ucode=liencese[2];
                log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取的头部license  base64解完码分割完数组：：：：：："+ JSON.toJSONString(liencese));
            }
        } catch (Exception e) {
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取的头部license  base64解完码分割异常：：：：：："+ e.getMessage());
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10039.getCode()+"");
        }
        log.info("业务请求["+apicode+"]：:*******用户："+ucode+"在客户端："+clientId+"进行了"+apicode+"（"+request.getRequestURI()+"）业务操作:::::设备信息："+JSON.toJSONString(userAgent)+"*********");
        // 校验放行权限
        System.out.println(request.getServletPath());
        if (request.getServletPath().endsWith("/zpzOauthOauthClientDetails/trace")){
            // 放行
            return true;
        }
        int releaseAccess = 0;
        try {
            releaseAccess = zpzOauthFilterReleaseAccess.isReleaseAccess(clientId, request);
        } catch (Exception e) {
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：校验放行权限失败：：：：：："+ e.getMessage());
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10024.getCode()+"");
        }
        if (releaseAccess==0){
            log.info("业务请求["+apicode+"]：:***外部三方放行权限******"+request.getRequestURI());
            return true;
        }
        if (!StringUtils.isNotBlank(lience)){
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：license为空：：：：：："+ clientId);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10023.getCode()+"");
        }
        //请求头必须携带clientId
        if (!StringUtils.isNotBlank(clientId)){
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：license 中clientId为空：：：：：："+ clientId);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10026.getCode()+"");
        }
        //请求头必须携带apicode
        if (!StringUtils.isNotBlank(apicode)){
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：license 中apicode为空：：：：：："+ apicode);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10028.getCode()+"");
        }
        //请求头必须携带ucode
        if (!StringUtils.isNotBlank(ucode)){
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：license 中ucode为空：：：：：："+ ucode);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10027.getCode()+"");
        }
        if (releaseAccess==1){
            log.info("业务请求["+apicode+"]：:****内部放行权限******"+request.getRequestURI());
            return true;
        }
        if (request.getServletPath().endsWith("/zpzOauthOauthClientDetails/trace")){

        }
        //解码jwt校验accesstoken和ucode
        Map<String, String> jwt = ZpzOauthDecodeJwt.getUserInfo(request);
        if (jwt==null){
            String msg = "业务请求["+apicode+"]：::::::::"+ ZpzOauthResultStatus.GATE_WAY_10035.getMessage()+"::::::::::";
            request.setAttribute("javax.servlet.error.exception","[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10035.getCode()+"");
            log.info(msg);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10035.getCode()+"");
        }
        String grantType = jwt.get("grantType");
        String jwt_token=jwt.get("jwt_token");
        String redis_token = null;
        if (StringUtils.isNotBlank(grantType) && grantType.equals("client_credentials")){
            //获取缓存token
            String access_token_key=APPLICATION_NAME+"_"+ ZpzOauthCommonConstant.CLIENT_INFO+":client_id:"+clientId+":access_token";
            redis_token = zpzOauthRedisService.get(access_token_key);
        } else{
            log.info("业务请求["+apicode+"]：:：：：：：：用户请求携带的token解析结果："+JSON.toJSONString(jwt));
            String usercode = jwt.get("ucode");
            if (!StringUtils.isNotBlank(usercode)){
                log.info("业务请求["+apicode+"]：:::::::::"+ ZpzOauthResultStatus.GATE_WAY_10035.getMessage()+"::::::::::");
                throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10035.getCode()+"");
            }
            if (!ucode.equals(usercode)){
                log.info("业务请求["+apicode+"]：::::::::"+ ZpzOauthResultStatus.GATE_WAY_10036.getMessage()+"::::::::::");
                throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10036.getCode()+"");
            }
            //获取缓存token
            String access_token_key=APPLICATION_NAME+"_"+ ZpzOauthCommonConstant.CLIENT_INFO+":client_id:"+clientId+":user:"+usercode+":access_token";
            redis_token = zpzOauthRedisService.get(access_token_key);
        }
        if (!StringUtils.isNotBlank(redis_token)||!StringUtils.isNotBlank(jwt_token)){
            log.info("业务请求["+apicode+"]：::::::::"+ ZpzOauthResultStatus.GATE_WAY_10038.getMessage()+"::::::::::");
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10038.getCode()+"");
        }
        if (!redis_token.equals(jwt_token)){
            log.info("业务请求["+apicode+"]：::::::::"+ ZpzOauthResultStatus.GATE_WAY_10037.getMessage()+"::::::::::token:::"+redis_token);
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10037.getCode()+"");
        }
        /***
         * 此处添加白名单过滤
         * */
       //获取客户端对应的权限角色authorities
        List<SimpleGrantedAuthority> authorityList = null;
        try {
            authorityList = (List<SimpleGrantedAuthority>) authentication.getAuthorities();
        } catch (Exception e) {
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取头部的授权角色码失败：：：：：："+ e.getMessage());
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10029.getCode()+"");
        }
        boolean flag = false;
        //下面是校验 客户端是否有访问权限
        if (authorityList == null && authorityList.size() <= 0) {
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取头部的授权角色码为空：：：：：：");
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10029.getCode()+"");
        }
        List<String> rolecodes=new ArrayList<>();
        for (SimpleGrantedAuthority authority : authorityList) {
            String roleId = authority.getAuthority();
            if (roleId.equals("ROLE_ANONYMOUS")){
                log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取头部的授权角色码中包含了ROLE_ANONYMOUS：：：：：："+roleId);
                throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10030.getCode()+"");
            }
            rolecodes.add(roleId);
        }
        //如客户端模式下，获取当前客户端下的角色权限域，如果是用户密码模式（授权码、用户名密码）下的当前用户的角色权限域
        List<GetAccessByRoleVo> accessByRoleVos = null;
        try {
            accessByRoleVos = getAccessByRoleVos(clientId, rolecodes,ucode);
        } catch (Exception e) {
            log.info("业务请求["+apicode+"]：:：：：：权限认证：：：获取头部的授权授权码失败：：：：：："+e.getMessage());
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10031.getCode()+"");
        }
        // method = ALL 中转服务
        try {
            if (accessByRoleVos!=null&&accessByRoleVos.size()>0){
                log.info(JSON.toJSONString(accessByRoleVos));
                for (GetAccessByRoleVo fap : accessByRoleVos) {
                    boolean notBlank = StringUtils.isNotBlank(fap.getMethodUrl());
                    String methodUrl = fap.getMethodUrl();
                    String requestURI = request.getRequestURI();
                    boolean match = antPathMatcher.match(methodUrl, requestURI);
                    if (notBlank&&match&& (fap.getMethodType().equals("ALL") ? true : request.getMethod().equalsIgnoreCase(fap.getMethodType()))) {
                        flag = true;
                        break;
                    }
                }
            }
        } catch (Exception e) {
            log.info("业务请求["+apicode+"]：:::权限认证失败：异常了"+e.getMessage()+":::::当前请求路径为："+request.getRequestURI()+"::::");
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10040.getCode()+""+e.getMessage());
        }
        if (!flag){
            log.info("业务请求["+apicode+"]：:::权限认证失败："+flag+":::::当前请求路径为："+request.getRequestURI()+"::::");
            throw new Exception("[ZPZ_DEFINED_OAUTH2_PREPOSITION_EXCEPTION]"+ZpzOauthResultStatus.GATE_WAY_10032.getCode()+"");
        }
        return flag;
    }

    @Override
    public List<GetAccessByRoleVo> getAccessByClient() throws Exception {
        String client_access_key="client_all_access:"+APPLICATION_NAME;
        List<GetAccessByRoleVo> res= zpzOauthRedisService.getList(client_access_key,GetAccessByRoleVo.class);
        if (res==null||res.size()<=0){
            FrameAccess fa=new FrameAccess();
            fa.setDelFlag(0);
            List<FrameAccess> frameAccesses = zpzOauthFrameAccessMapper.select(fa);
            List<GetAccessByRoleVo> ac = ZpzOauthBeanUtils.copyListProperties(frameAccesses, GetAccessByRoleVo.class);
            FrameReleaseAccess fra=new FrameReleaseAccess();
            fra.setDelFlag(0);
            List<FrameReleaseAccess> frameReleaseAccesses = zpzOauthFrameReleaseAccessMapper.select(fra);
            List<GetAccessByRoleVo> ra = ZpzOauthBeanUtils.copyListProperties(frameReleaseAccesses, GetAccessByRoleVo.class);
            ac.addAll(ra);
            zpzOauthRedisService.setList(client_access_key, ac);
            redisTemplate.expire(client_access_key, ZpzOauthCommonConstant.RELEASE_ACCESS_HOURS, TimeUnit.HOURS);
            return ac;
        }
        return res;
    }

    @Override
    public List<GetAccessByRoleVo> getAccessByRoleVos(String clientId,List<String> codes,String ucode)throws Exception{
        OauthClientDetails client=zpzOauthRequestUtil.getClient(clientId);
        if (client==null){
            throw new Exception("无效的客户端");
        }
        Integer accessType=client.getAccessType();
        if (accessType==null){
            throw new Exception("没有配置客户端限定用户权限类型");
        }
        String client_access_key=null;
        if (accessType== ZpzOauthCommonConstant.CLIENT_ACCESS_TYPE_1){
            // 客户端控制权限
            client_access_key=APPLICATION_NAME+"_"+"client_access:"+accessType+":"+clientId;
        }else if (accessType== 0){
            // 角色控制权限
            client_access_key=APPLICATION_NAME+"_"+"client_access:"+accessType+":"+clientId+":"+ucode;
        }else if (accessType== 2){
            // 部门控制权限
            client_access_key=APPLICATION_NAME+"_"+"client_access:"+accessType+":"+clientId+":"+ucode;
        }
        List<GetAccessByRoleVo> result=new ArrayList<>();
        List<GetAccessByRoleVo> res= zpzOauthRedisService.getList(client_access_key,GetAccessByRoleVo.class);
        StringBuffer sb = new StringBuffer("");
        if (res==null){
            for (String r : codes) {
                sb.append("'" + r + "',");
            }
            String roleCodes = sb.toString();
            if (roleCodes.endsWith(",")) {
                roleCodes = roleCodes.substring(0, roleCodes.lastIndexOf(","));
            }
            ZpzOauthResultMsg<List<GetAccessByRoleVo>> resultMsg =null;
            if (accessType==0||accessType==1){
                resultMsg = zpzOauthFrameAccessService.getAccessByRole(roleCodes);
            } else if (accessType==2){
                resultMsg = zpzOauthFrameAccessService.getAccessByDepartment(roleCodes);
            }
            if (resultMsg.getCode()==0){
                if (resultMsg.getData()!=null){
                    result=resultMsg.getData();
                }
                zpzOauthRedisService.setList(client_access_key, result);
                redisTemplate.expire(client_access_key, ZpzOauthCommonConstant.RELEASE_ACCESS_HOURS, TimeUnit.HOURS);
            }
        }else{
            result=res;
        }
        return result;
    }
}
